package com.clp.protocol.iec104.client.pipeline.state.data;

import com.clp.protocol.iec104.apdu.asdu.Cot;
import com.clp.protocol.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.apdu.asdu.infoobj.InfoObj;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.C_DC_NA_1_InfoElem;
import com.clp.protocol.iec104.apdu.asdu.infoobj.infoelem.C_SC_NA_1_InfoElem;
import com.clp.protocol.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.client.async.sendapdu.SendTcExecuteMasterPromise;
import com.clp.protocol.iec104.client.async.sendapdu.SendTcSelectMasterPromise;
import com.clp.protocol.iec104.client.pipeline.MPipelineManager;
import com.clp.protocol.iec104.client.pipeline.state.MasterPromiseRegister;
import com.clp.protocol.iec104.definition.Tc;
import com.clp.protocol.iec104.definition.TypeTag;
import com.clp.protocol.core.pdu.nbytepdu.FailedToSendFrameException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

@Slf4j
public class MTcDataHandler extends AbstractMDataStateHandler implements MasterPromiseRegister {
    private final PtStateMap ptStateMap;
    private final PromiseMap<SendTcSelectMasterPromise> selectPromiseMap;
    private final PromiseMap<SendTcExecuteMasterPromise> executePromiseMap;

    public MTcDataHandler(MPipelineManager manager) {
        super(manager);
        this.ptStateMap = new PtStateMap();
        this.selectPromiseMap = new PromiseMap<>();
        this.executePromiseMap = new PromiseMap<>();
    }

    @Override
    protected void resetState() {
        this.ptStateMap.reset();
    }

    @Override
    protected void afterResetState() {

    }

    @Override
    protected IAsdu updateStateByRecv(IAsdu iAsdu) throws Exception {
        if (iAsdu == null) return iAsdu;

        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        int rtuAddr = iAsdu.getRtuAddr();

        InfoObj infoObj;
        int addr;
        Tc.CmdType cmdType;
        PtStateImpl ptStateImpl;

        switch (typeTag) { // 类型标识
            case C_SC_NA_1:
                infoObj = iAsdu.getInfoObjs().get(0);
                // 公共地址
                addr = infoObj.getAddr();
                // 命令类型
                cmdType = iAsdu.getInfoObjs().get(0).getInfoElem().castTo(C_SC_NA_1_InfoElem.class).getCmdType();
                ptStateImpl = ptStateMap.get(rtuAddr, addr);

                switch (cot.getCause()) { // 传输原因
                    case COT_ACTCON:
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                                    log.error("收到的单点遥控选择确认报文不存在对应的过程！({})", addr);
                                    break;
                                }
                                if (ptStateImpl.state != State.SELECT) {
                                    log.warn("接收到无效的单点遥控选择确认报文({})", addr);
                                    break;
                                }
                                // 校验通过
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        ptStateImpl.setState(State.SELECT_ACK_YES).setLastSelectAckYesTime(new Date());
                                        log.info("收到单点遥控选择肯定确认报文({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false);
                                            promise.setSuccess();
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        ptStateImpl.setState(State.SELECT_ACK_NO).setLastSelectAckNoTime(new Date());
                                        log.info("收到单点遥控选择否定确认报文({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到单点遥控选择否定确认报文");
                                            promise.setFailure(new RuntimeException("收到单点遥控选择否定确认报文"));
                                            return true;
                                        });

                                        // 从列表中移除
                                        ptStateImpl.setState(State.END);
                                        ptStateMap.remove(rtuAddr, addr);
                                        log.info("已移除单点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                                        break;
                                }
                                break;
                            case EXECUTE:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                                    log.error("收到的单点遥控执行确认报文不存在对应的过程！({})", addr);
                                    break;
                                }
                                if (ptStateImpl.state != State.EXECUTE) {
                                    log.warn("接收到无效的单点遥控执行确认报文({})", addr);
                                    break;
                                }
                                // 校验通过
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        ptStateImpl.setState(State.EXECUTE_ACK_YES).setLastExecuteAckYesTime(new Date());
                                        log.info("收到单点遥控执行肯定确认报文({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false);
                                            return false;
                                        });
                                        break;
                                    case PN_NO:
                                        ptStateImpl.setState(State.EXECUTE_ACK_NO).setLastExecuteAckNoTime(new Date());
                                        log.info("收到单点遥控执行否定确认报文({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到单点遥控执行否定确认报文");
                                            promise.setFailure(new RuntimeException("收到单点遥控执行否定确认报文"));
                                            return true;
                                        });

                                        // 从列表中移除
                                        ptStateImpl.setState(State.END);
                                        ptStateMap.remove(rtuAddr, addr);
                                        log.info("已移除单点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                                        break;
                                }
                                break;
                        }
                        break;
                    case COT_ACTTERM:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                            log.error("收到的单点遥控激活终止报文不存在对应的过程！({})", addr);
                            break;
                        }
                        if (ptStateImpl.state != State.EXECUTE_ACK_YES) {
                            log.warn("接收到无效的单点遥控激活重置报文({})", addr);
                            break;
                        }
                        // 校验通过
                        ptStateImpl.setState(State.END).setLastExecuteFinishedTime(new Date());
                        log.info("收到单点遥控激活终止报文({})", addr);
                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                            promise.getRes().setRecvEnd(true);
                            promise.setSuccess();
                            return true;
                        });

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除单点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                        break;
                    case COT_UNKNOW_TYPE:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                            log.warn("收到的单点遥控报文不存在对应的过程！({})", addr);
                            break;
                        }
                        // 校验通过
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                if (ptStateImpl.state != State.SELECT) {
                                    log.warn("接收到无效的单点遥控报文（未知的类型标识）({})", addr);
                                    break;
                                }
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到单点遥控选择肯定确认报文（未知的类型标识）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false).setFailDesc("收到单点遥控选择肯定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到单点遥控选择肯定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到单点遥控选择否定确认报文（未知的类型标识）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到单点遥控选择否定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到单点遥控选择否定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                            case EXECUTE:
                                if (ptStateImpl.state != State.EXECUTE) {
                                    log.warn("接收到无效的单点遥控报文（未知的类型标识）({})", addr);
                                    break;
                                }
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到单点遥控执行肯定确认报文（未知的类型标识）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到单点遥控执行肯定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到单点遥控执行肯定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到单点遥控执行否定确认报文（未知的类型标识）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(true).setRecvEnd(false).setFailDesc("收到单点遥控执行否定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到单点遥控执行否定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                        }
                        ptStateImpl.setState(State.END);

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除单点遥控过程({})，剩余过程数：({})", addr, ptStateMap.size());
                        break;
                    case COT_UNKNOW_INF:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                            log.warn("收到的单点遥控报文不存在对应的过程！({})", addr);
                            break;
                        }
                        if (ptStateImpl.state != State.SELECT || ptStateImpl.state != State.EXECUTE) {
                            log.warn("接收到无效的单点遥控报文（未知的信息体对象地址）({})", addr);
                            break;
                        }
                        // 校验通过
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到单点遥控选择肯定确认报文（未知的信息体对象地址）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false).setFailDesc("收到单点遥控选择肯定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到单点遥控选择肯定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到单点遥控选择否定确认报文（未知的信息体对象地址）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到单点遥控选择否定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到单点遥控选择否定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                            case EXECUTE:
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到单点遥控执行肯定确认报文（未知的信息体对象地址）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到单点遥控执行肯定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到单点遥控执行肯定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到单点遥控执行否定确认报文（未知的信息体对象地址）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(true).setRecvEnd(false).setFailDesc("收到单点遥控执行否定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到单点遥控执行否定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                        }
                        ptStateImpl.setState(State.END);

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除单点遥控过程({})，剩余过程数：({})", addr, ptStateMap.size());
                        break;
                }
                break;
            case C_DC_NA_1:
                infoObj = iAsdu.getInfoObjs().get(0);
                // 公共地址
                addr = infoObj.getAddr();
                // 命令类型
                cmdType = iAsdu.getInfoObjs().get(0).getInfoElem().castTo(C_DC_NA_1_InfoElem.class).getCmdType();

                ptStateImpl = ptStateMap.get(rtuAddr, addr);
                switch (cot.getCause()) { // 传输原因
                    case COT_ACTCON:
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                                    log.error("收到的双点遥控选择确认报文不存在对应的过程！({})", addr);
                                    break;
                                }
                                if (ptStateImpl.state != State.SELECT) {
                                    log.warn("接收到无效的双点遥控选择确认报文({})", addr);
                                    break;
                                }
                                // 校验通过
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        ptStateImpl.setState(State.SELECT_ACK_YES).setLastSelectAckYesTime(new Date());
                                        log.info("收到双点遥控选择肯定确认报文({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false);
                                            promise.setSuccess();
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        ptStateImpl.setState(State.SELECT_ACK_NO).setLastSelectAckNoTime(new Date());
                                        log.info("收到双点遥控选择否定确认报文({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到双点遥控选择否定确认报文");
                                            promise.setFailure(new RuntimeException("收到双点遥控选择否定确认报文"));
                                            return true;
                                        });

                                        // 从列表中移除
                                        ptStateImpl.setState(State.END);
                                        ptStateMap.remove(rtuAddr, addr);
                                        log.info("已移除双点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                                        break;
                                }
                                break;
                            case EXECUTE:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                                    log.error("收到的双点遥控执行确认报文不存在对应的过程！({})", addr);
                                    break;
                                }
                                if (ptStateImpl.state != State.EXECUTE) {
                                    log.warn("接收到无效的双点遥控执行确认报文({})", addr);
                                    break;
                                }
                                // 校验通过
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        ptStateImpl.setState(State.EXECUTE_ACK_YES).setLastExecuteAckYesTime(new Date());
                                        log.info("收到双点遥控执行肯定确认报文({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false);
                                            return false;
                                        });
                                        break;
                                    case PN_NO:
                                        ptStateImpl.setState(State.EXECUTE_ACK_NO).setLastExecuteAckNoTime(new Date());
                                        log.info("收到双点遥控执行否定确认报文({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到双点遥控执行否定确认报文({})");
                                            promise.setFailure(new RuntimeException("收到双点遥控执行否定确认报文({})"));
                                            return true;
                                        });

                                        // 从列表中移除
                                        ptStateImpl.setState(State.END);
                                        ptStateMap.remove(rtuAddr, addr);
                                        log.info("以移除双点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                                        break;
                                }
                                break;
                        }
                        break;
                    case COT_ACTTERM:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                            log.error("收到的双点遥控激活终止报文不存在对应的过程！({})", addr);
                            break;
                        }
                        if (ptStateImpl.state != State.EXECUTE_ACK_YES) {
                            log.warn("接收到无效的双点遥控激活重置报文({})", addr);
                            break;
                        }
                        // 校验通过
                        ptStateImpl.setState(State.END).setLastExecuteFinishedTime(new Date());
                        log.info("收到双点遥控激活终止报文({})", addr);
                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                            promise.getRes().setRecvEnd(true);
                            promise.setSuccess();
                            return true;
                        });

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除双点遥控过程({}), 剩余过程数：({})", addr, ptStateMap.size());
                        break;
                    case COT_UNKNOW_TYPE:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                            log.warn("收到的双点遥控报文不存在对应的过程！({})", addr);
                            break;
                        }
                        // 校验通过
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                if (ptStateImpl.state != State.SELECT) {
                                    log.warn("接收到无效的双点遥控报文（未知的类型标识）({})", addr);
                                    break;
                                }
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到双点遥控选择肯定确认报文（未知的类型标识）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false).setFailDesc("收到双点遥控选择肯定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到双点遥控选择肯定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到双点遥控选择否定确认报文（未知的类型标识）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到双点遥控选择否定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到双点遥控选择否定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                            case EXECUTE:
                                if (ptStateImpl.state != State.EXECUTE) {
                                    log.warn("接收到无效的双点遥控报文（未知的类型标识）({})", addr);
                                    break;
                                }
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到双点遥控执行肯定确认报文（未知的类型标识）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到双点遥控执行肯定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到双点遥控执行肯定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到双点遥控执行否定确认报文（未知的类型标识）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(true).setRecvEnd(false).setFailDesc("收到双点遥控执行否定确认报文（未知的类型标识）");
                                            promise.setFailure(new RuntimeException("收到双点遥控执行否定确认报文（未知的类型标识）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                        }
                        ptStateImpl.setState(State.END);

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除双点遥控过程({})，剩余过程数：({})", addr, ptStateMap.size());
                        break;
                    case COT_UNKNOW_INF:
                        // 校验
                        if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                            log.warn("收到的双点遥控报文不存在对应的过程！({})", addr);
                            break;
                        }
                        if (ptStateImpl.state != State.SELECT || ptStateImpl.state != State.EXECUTE) {
                            log.warn("接收到无效的双点遥控报文（未知的信息体对象地址）({})", addr);
                            break;
                        }
                        // 校验通过
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到双点遥控选择肯定确认报文（未知的信息体对象地址）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(true).setRecvSelectAckNo(false).setFailDesc("收到双点遥控选择肯定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到双点遥控选择肯定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到双点遥控选择否定确认报文（未知的信息体对象地址）({})", addr);
                                        selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(true).setFailDesc("收到双点遥控选择否定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到双点遥控选择否定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                            case EXECUTE:
                                switch (cot.getPn()) { // 肯定或否定确认
                                    case PN_YES:
                                        log.warn("收到双点遥控执行肯定确认报文（未知的信息体对象地址）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(true).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("收到双点遥控执行肯定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到双点遥控执行肯定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                    case PN_NO:
                                        log.warn("收到双点遥控执行否定确认报文（未知的信息体对象地址）({})", addr);
                                        executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                            promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(true).setRecvEnd(false).setFailDesc("收到双点遥控执行否定确认报文（未知的信息体对象地址）");
                                            promise.setFailure(new RuntimeException("收到双点遥控执行否定确认报文（未知的信息体对象地址）"));
                                            return true;
                                        });
                                        break;
                                }
                                break;
                        }
                        ptStateImpl.setState(State.END);

                        // 从列表中移除
                        ptStateMap.remove(rtuAddr, addr);
                        log.info("已移除双点遥控过程({})，剩余过程数：({})", addr, ptStateMap.size());
                        break;
                }
                break;
        }

        return iAsdu;
    }

    @Override
    protected IAsdu updateStateBySend(IAsdu iAsdu) throws Exception {
        if (iAsdu == null) return iAsdu;

        TypeTag typeTag = iAsdu.getTypeTag();
        Cot cot = iAsdu.getCot();
        int rtuAddr = iAsdu.getRtuAddr();

        InfoObj infoObj;
        int addr;
        Tc.CmdType cmdType;
        PtStateImpl ptStateImpl;

        switch (typeTag) {
            case C_SC_NA_1: // 单点命令-遥控
                infoObj = iAsdu.getInfoObjs().get(0);
                // 公共地址
                addr = infoObj.getAddr();
                // 命令类型
                final C_SC_NA_1_InfoElem theInfoElem = iAsdu.getInfoObjs().get(0).getInfoElem().castTo(C_SC_NA_1_InfoElem.class);
                cmdType = theInfoElem.getCmdType();
                ptStateImpl = ptStateMap.get(rtuAddr, addr);

                switch (cot.getCause()) { // 传输原因
                    case COT_ACT:
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                // 校验
                                if (ptStateImpl != null) {
                                    log.warn("已存在相同信息体地址的单点遥控命令过程，放弃本次选择命令({})", addr);
                                    selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("已存在相同信息体地址的单点遥控命令过程");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                // 校验通过，添加新测点的遥控状态
                                ptStateMap.put(rtuAddr, addr,
                                        new PtStateImpl(1, rtuAddr, addr, theInfoElem.isSwitchOn()).setState(State.SELECT).setLastSelectTime(new Date())
                                );
                                log.info("发送新的单点遥控选择报文({})", addr);
                                break;
                            case EXECUTE:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 1) {
                                    log.warn("发送的单点遥控执行报文不存在对应的过程，该报文已放弃发送！({})", addr);
                                    executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("已存在相同信息体地址的单点遥控命令过程");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                if (ptStateImpl.state != State.SELECT_ACK_YES) {
                                    log.error("未收到单点遥控选择肯定确认报文，放弃本次遥控执行命令({})", addr);
                                    executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("未收到单点遥控选择肯定确认报文，放弃本次遥控执行命令");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                // 校验通过
                                ptStateImpl.setState(State.EXECUTE).setLastExecuteTime(new Date());
                                log.info("发送单点遥控执行报文({})", addr);
                                break;
                        }
                        break;
                }
                break;
            case C_DC_NA_1: // 双点命令-遥控
                infoObj = iAsdu.getInfoObjs().get(0);
                // 公共地址
                addr = infoObj.getAddr();
                // 命令类型
                final C_DC_NA_1_InfoElem theInfoElem2 = iAsdu.getInfoObjs().get(0).getInfoElem().castTo(C_DC_NA_1_InfoElem.class);
                cmdType = theInfoElem2.getCmdType();
                ptStateImpl = ptStateMap.get(rtuAddr, addr);

                switch (cot.getCause()) { // 传输原因
                    case COT_ACT:
                        switch (cmdType) { // 命令类型
                            case SELECT:
                                // 校验
                                if (ptStateImpl != null) {
                                    log.error("已存在相同信息体地址的双点遥控命令过程，放弃本次选择命令({})", addr);
                                    selectPromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("已存在相同信息体地址的双点遥控命令过程");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                // 校验通过
                                ptStateMap.put(rtuAddr, addr,
                                        new PtStateImpl(2, rtuAddr, addr, theInfoElem2.isSwitchOn()).setState(State.SELECT).setLastSelectTime(new Date())
                                );
                                log.info("发送新的双点遥控选择报文({})", addr);
                                break;
                            case EXECUTE:
                                // 校验
                                if (ptStateImpl == null || ptStateImpl.ptType != 2) {
                                    log.error("发送的双点遥控执行报文不存在对应的过程，该报文已放弃发送！({})", addr);
                                    executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("已存在相同信息体地址的双点遥控命令过程");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                if (ptStateImpl.state != State.SELECT_ACK_YES) {
                                    log.error("未收到双点遥控选择肯定确认报文，放弃本次遥控执行命令({})", addr);
                                    executePromiseMap.removeIf(rtuAddr, addr, promise -> {
                                        promise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("未收到双点遥控选择肯定确认报文，放弃本次遥控执行命令");
                                        promise.setFailure(new FailedToSendFrameException(iAsdu));
                                        return true;
                                    });
                                    return null;
                                }
                                // 校验通过
                                ptStateImpl.setState(State.EXECUTE).setLastExecuteTime(new Date());
                                log.info("发送双点遥控执行报文({})", addr);
                                break;
                        }
                        break;
                }
                break;
        }
        return iAsdu;
    }

    @Nullable
    @Override
    protected ScheduledTask getScheduledTask() {
        return new ScheduledTask(0, 1, TimeUnit.SECONDS) {
            @Override
            public void run() {
                if (!inMaster().controlInfo().isInitCompleted()) {
                    return;
                }
                for (Iterator<Map.Entry<String, PtStateImpl>> iterator = ptStateMap.psMap.entrySet().iterator(); iterator.hasNext();) {
                    Map.Entry<String, PtStateImpl> entry = iterator.next();
                    PtStateImpl ptStateImpl = entry.getValue();
                    switch (ptStateImpl.state) {
                        case SELECT:
                            // 遥控确认选择超时情况的处理
                            if (new Date().getTime() - ptStateImpl.lastSelectTime.getTime() > 10 * 1000) {
                                log.error("发送" + (ptStateImpl.ptType == 1 ? "单点" : "双点") + "遥控选择10秒后未收到确认，放弃本次遥控请求");
                                selectPromiseMap.removeIf(ptStateImpl.rtuAddr, ptStateImpl.addr, promise -> {
                                    promise.getRes().setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("遥控选择确认超时");
                                    promise.setFailure(new RuntimeException("遥控选择确认超时"));
                                    return true;
                                });
                                ptStateImpl.state = State.END;
                                iterator.remove();
                                log.error("因超时移除遥控过程({}), 移除后剩余遥控过程数：({})", ptStateImpl.addr, ptStateMap.size());
                            }
                            break;
                        case SELECT_ACK_YES:
                            // 处理发送遥控执行超时
                            if (new Date().getTime() - ptStateImpl.lastSelectAckYesTime.getTime() > 60 * 1000) {
                                log.error("收到遥控选择肯定确认报文60秒后未发送遥控执行，放弃本次遥控请求({})", ptStateImpl.addr);
                                ptStateImpl.state = State.END;
                                iterator.remove();
                                log.error("因超时移除遥控过程({}), 移除后剩余过程数：({})", ptStateImpl.addr, ptStateMap.size());
                            }
                            break;
                        case SELECT_ACK_NO:
                            // 进入该状态说明接收到了遥控选择否定确认/未知的信息体地址，要重置状态（已经重置）
                            break;
                        case EXECUTE:
                            // 遥控执行确认超时情况的处理
                            if (new Date().getTime() - ptStateImpl.lastExecuteTime.getTime() > 10 * 1000) {
                                log.error("发送遥控执行10秒后未收到确认，放弃本次遥控请求({})", ptStateImpl.addr);
                                executePromiseMap.removeIf(ptStateImpl.rtuAddr, ptStateImpl.addr, promise -> {
                                    promise.getRes().setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false).setFailDesc("发送遥控执行10秒后未收到确认，放弃本次遥控请求");
                                    promise.setFailure(new RuntimeException("遥控执行确认超时"));
                                    return true;
                                });
                                ptStateImpl.state = State.END;
                                iterator.remove();
                                log.error("因超时移除遥控过程({}), 移除后剩余过程数({})", ptStateImpl.addr, ptStateMap.size());
                            }
                            break;
                        case EXECUTE_ACK_YES:
                            // 进入该状态说明接收到了遥控执行确认报文，需要进行遥控执行结束超时情况的处理
                            if (new Date().getTime() - ptStateImpl.lastExecuteAckYesTime.getTime() > 10 * 1000) {
                                log.error("收到遥控执行确认10秒后未收到终止确认，放弃本次遥控请求");
                                executePromiseMap.removeIf(ptStateImpl.rtuAddr, ptStateImpl.addr, promise -> {
                                    promise.getRes().setRecvEnd(false).setFailDesc("收到遥控执行确认10秒后未收到终止确认，放弃本次遥控请求");
                                    promise.setFailure(new RuntimeException("遥控终止确认超时"));
                                    return true;
                                });
                                ptStateImpl.state = State.END;
                                iterator.remove();
                                log.error("因超时移除遥控过程({}), 移除后剩余过程数：({})", ptStateImpl.addr, ptStateMap.size());
                            }
                            break;
                        case EXECUTE_ACK_NO:
                            // 进入该状态说明接收到了遥控执行否定确认/未知的信息体对象地址 报文，要重置状态
                            break;
                        case END:
                            // 进入该状态说明接收到了遥控执行终止报文，本次请求顺利结束
                            break;
                    }
                }
            }
        };
    }

    @Override
    public <V> void register(MasterPromise<V> sendPromise) {
        if (sendPromise == null || sendPromise.getRes() == null) return;

        if (sendPromise instanceof SendTcSelectMasterPromise) {
            SendTcSelectMasterPromise promise = (SendTcSelectMasterPromise) sendPromise;
            selectPromiseMap.put(promise.getRtuAddr(), promise.getAddr(), promise);
            return;
        }
        if (sendPromise instanceof SendTcExecuteMasterPromise) {
            SendTcExecuteMasterPromise promise = (SendTcExecuteMasterPromise) sendPromise;
            executePromiseMap.put(promise.getRtuAddr(), promise.getAddr(), promise);
        }
    }

    public boolean isDoingOnePointTcSelect(int infoObjAddr) {
        synchronized (ptStateMap) {
            PtStateImpl ptStateImpl = ptStateMap.get(inMaster().localRtuAddr(), infoObjAddr);
            return ptStateImpl != null && ptStateImpl.isOnePoint() && ptStateImpl.isDoingSelect();
        }
    }

    public boolean isDoingTwoPointTcSelect(int infoObjAddr) {
        synchronized (ptStateMap) {
            PtStateImpl ptStateImpl = ptStateMap.get(inMaster().localRtuAddr(), infoObjAddr);
            return ptStateImpl != null && ptStateImpl.isTwoPoint() && ptStateImpl.isDoingSelect();
        }
    }

    public boolean isDoingOnePointTcExecute(int infoObjAddr) {
        synchronized (ptStateMap) {
            PtStateImpl ptStateImpl = ptStateMap.get(inMaster().localRtuAddr(), infoObjAddr);
            return ptStateImpl != null && ptStateImpl.isOnePoint() && ptStateImpl.isDoingExecute();
        }
    }

    public boolean isDoingTwoPointTcExecute(int infoObjAddr) {
        synchronized (ptStateMap) {
            PtStateImpl ptStateImpl = ptStateMap.get(inMaster().localRtuAddr(), infoObjAddr);
            return ptStateImpl != null && ptStateImpl.isTwoPoint() && ptStateImpl.isDoingExecute();
        }
    }

    public List<Integer> listAllDoingTcAddrs() {
        List<Integer> list = new ArrayList<>();
        ptStateMap.forEach((rtuAddrAddr, ptStateImpl) -> list.add(ptStateImpl.addr));
        return list;
    }

    public List<PtState> listAllDoingTcPtStates() {
        return new ArrayList<>(ptStateMap.psMap.values());
    }

    @Getter
    @AllArgsConstructor
    public enum State {
        /**
         * 遥控选择
         */
        SELECT("选择中"),
        /**
         * 遥控选择肯定确认
         */
        SELECT_ACK_YES("选择肯定确认"),
        /**
         * 遥控选择否定确认
         */
        SELECT_ACK_NO("选择否定确认"),
        /**
         * 遥控执行
         */
        EXECUTE("执行中"),
        /**
         * 遥控执行肯定确认
         */
        EXECUTE_ACK_YES("执行肯定确认"),
        /**
         * 遥控执行否定确认
         */
        EXECUTE_ACK_NO("执行否定确认"),
        /**
         * 遥控执行结束
         */
        END("空闲");

        private final String desc;
    }

    public interface PtState {
        int getRtuAddr();
        int getAddr();
        Tc.Type getType();
        boolean isSwitchOn();
        State getState();
    }

    /**
     * 单点、双端遥控 每一个点的状态
     */
    @Data
    @Accessors(chain = true)
    private static class PtStateImpl implements PtState {
        private final int rtuAddr;
        private final int addr;
        private volatile State state;
        private final int ptType; // 1为单点；2为双点
        private final boolean isSwitchOn;

        private volatile Date lastSelectTime;
        private volatile Date lastSelectAckYesTime;
        private volatile Date lastSelectAckNoTime;
        private volatile Date lastExecuteTime;
        private volatile Date lastExecuteAckYesTime;
        private volatile Date lastExecuteAckNoTime;
        private volatile Date lastExecuteFinishedTime;

        public PtStateImpl(int ptType, int rtuAddr, int addr, boolean isSwitchOn) {
            this.rtuAddr = rtuAddr;
            this.addr = addr;
            this.ptType = ptType;
            this.isSwitchOn = isSwitchOn;
            this.reset();
        }

        private void reset() {
            this.state = State.END;
            this.lastSelectTime = new Date();
            this.lastSelectAckYesTime = new Date();
            this.lastSelectAckNoTime = new Date();
            this.lastExecuteTime = new Date();
            this.lastExecuteAckYesTime = new Date();
            this.lastExecuteAckNoTime = new Date();
            this.lastExecuteFinishedTime = new Date();
        }

        public boolean isEndState() {
            return state == State.END;
        }

        public boolean isOnePoint() {
            return ptType == 1;
        }

        public boolean isTwoPoint() {
            return ptType == 2;
        }

        public boolean isDoingSelect() {
            return state == State.SELECT;
        }

        public boolean isDoingExecute() {
            return state == State.EXECUTE;
        }

        @Override
        public Tc.Type getType() {
            return ptType == 1 ? Tc.Type.ONE_POINT : Tc.Type.TWO_POINT;
        }

        @Override
        public boolean isSwitchOn() {
            return isSwitchOn;
        }
    }

    private static class PtStateMap {
        private Map<String, PtStateImpl> psMap;

        private PtStateMap() {
            this.psMap = new HashMap<>();
        }

        private String genKey(int rtuAddr, int addr) {
            return "" + rtuAddr + "-" + addr;
        }

        public synchronized PtStateImpl put(int rtuAddr, int addr, PtStateImpl ptStateImpl) {
            return psMap.put(genKey(rtuAddr, addr), ptStateImpl);
        }

        public synchronized PtStateImpl get(int rtuAddr, int addr) {
            return psMap.get(genKey(rtuAddr, addr));
        }

        public synchronized boolean containsKey(int rtuAddr, int addr) {
            return psMap.containsKey(genKey(rtuAddr, addr));
        }

        public synchronized PtStateImpl remove(int rtuAddr, int addr) {
            return psMap.remove(genKey(rtuAddr, addr));
        }

        public synchronized void forEach(BiConsumer<String, PtStateImpl> consumer) {
            psMap.forEach(consumer);
        }

        public synchronized int size() {
            return psMap.size();
        }

        public synchronized void reset() {
            psMap.clear();
        }
    }
}
